
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"
#include <cstdio>
#include <glad/glad.h>
#include <SDL2/SDL.h>
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL_opengles2.h>
#else
#include <SDL_opengl.h>
#endif

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtc/type_ptr.hpp>

#include "shader/Shader.h"
#include "Model.h"
#include "Camera.h"
#include <Singleton.h>
#include <Cache.h>
#include <Texture.h>
#include <chrono>
#include "File.h"
#include "ThreadPool.h"
#include "File.h"

using std::chrono::high_resolution_clock;
using std::chrono::milliseconds;

Camera camera(glm::vec3(0.0f, 5.0f, 10.0f));
glm::vec3 WorldUp(0.0f, 1.0f, 0.0f);

void ProcessSkeleton(Skeleton *skeleton, glm::mat4 offset, int tick, const char *anim_name1, const char *anim_name2, float blend)
{
	skeleton->Play2BlendAnimation(anim_name1, anim_name2, tick, blend);
	skeleton->offset = offset;
	skeleton->toDraw = true;
	skeleton->ProcessManagerData();
}

bool Processing = false;
bool ProcessOver = false;
std::vector<std::future<void> *> futures;

template <typename R>
bool is_ready(std::future<R> const &f)
{

	return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
}

unsigned int CreateFrameTexture(int width, int height)
{
	unsigned int texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	return texture;
}

unsigned int CreateDepthTexture(int width, int height)
{
	unsigned int texture;
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_2D, texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, NULL);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	return texture;
}

unsigned int CreateRenderBuffer(int width, int height)
{
	unsigned int renderBuffer;
	glGenRenderbuffers(1, &renderBuffer);
	glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
	glBindRenderbuffer(GL_RENDERBUFFER, 0);
	return renderBuffer;
}

void checkSDLError(int line = -1)
{
#ifndef NDEBUG
	const char *error = SDL_GetError();
	if (*error != '\0')
	{
		printf("SDL Error: %s\n", error);
		if (line != -1)
			printf(" + line: %i\n", line);
		SDL_ClearError();
	}
#endif
}

void sdldie(const char *msg)
{
	printf("%s: %s\n", msg, SDL_GetError());
	SDL_Quit();
	exit(1);
}

std::unordered_map<std::string, high_resolution_clock::time_point> times;
std::unordered_map<std::string, milliseconds> timesDuration;

void RecordTime(std::string tag)
{
	times[tag] = high_resolution_clock::now();
}

void StopRecord(std::string tag)
{
	high_resolution_clock::time_point now = high_resolution_clock::now();

	// t2 = high_resolution_clock::now();
	timesDuration[tag] = std::chrono::duration_cast<milliseconds>(now - times[tag]);
}

// Assets
ModelAsset *modelContext = nullptr;
SkeletonManager *skeletonManager = nullptr;
std::shared_ptr<SkeletonAsset> skeletonContext = nullptr;

std::vector<std::shared_ptr<Skeleton>> skeletons;
std::vector<std::shared_ptr<MeshInstance>> meshes;
std::vector<const char *> animNames;

std::vector<std::string> meshNames;
std::vector<bool> meshDraw;
int anim1index = 0;
int anim2index = 0;
const char *anim1;
const char *anim2;

std::vector<bool> BodyPartDraw;
std::vector<std::string> BodyPartNames = {"Head", "Torso", "Hip", "Thigh", "Leg", "Foot", "UpperArm", "LowerArm", "Hand"};
std::vector<MeshInstanceType> BodyPartType = {MeshInstanceType::Head, MeshInstanceType::Torso, MeshInstanceType::Hip,
											  MeshInstanceType::Thigh, MeshInstanceType::Leg, MeshInstanceType::Foot,
											  MeshInstanceType::UpperArm, MeshInstanceType::LowerArm, MeshInstanceType::Hand};

bool LoadModel(const std::string fileName)
{
	if (FileReader::exists(fileName))
	{
		modelContext = new ModelAsset(fileName);
		skeletonContext = modelContext->skeleton;
		skeletonManager = new SkeletonManager(modelContext->skeleton);

		// skeleton Instances
		skeletons.resize((*modelContext->animNames).size() * 1);
		for (int i = 0; i < skeletons.size(); i++)
		{
			skeletons[i] = skeletonManager->CreateSkeleton();
		}

		// mesh Instances
		meshes.resize(skeletonContext->orderedMeshes.size() * skeletons.size());
		{
			int i = 0;
			for (int j = 0; j < skeletons.size(); j++)
			{
				for (auto om : skeletonContext->orderedMeshes)
				{
					meshes[i] = om.second->CreateBindedInstance(skeletons[j]);
					i++;
				}
			}
		}

		// meshes draw control
		meshNames.resize(skeletonContext->orderedMeshes.size());
		{
			int i = 0;
			for (auto om : skeletonContext->orderedMeshes)
			{
				meshNames[i] = om.first.c_str();
				i++;
			}
		}

		meshDraw = std::vector<bool>(skeletonContext->orderedMeshes.size(), true);
		for (int i = 0; i < skeletonContext->orderedMeshes.size(); i++)
		{
			if (meshNames[i] == "body-link")
			{
				meshDraw[i] = false;
			}

			// 如果包含“-part"，则不显示
			if (meshNames[i].find("part_") != std::string::npos)
			{
				meshDraw[i] = false;
			}

			if (meshNames[i].find("wb-") != std::string::npos)
			{
				meshDraw[i] = false;
			}
		}

		// model animations control
		animNames.resize(skeletonContext->orderedAnimations.size());
		std::cout << "animNames size:" << skeletonContext->orderedAnimations.size() << std::endl;
		{
			int i = 0;
			for (auto &anim : skeletonContext->orderedAnimations)
			{
				animNames[i] = anim.second->name.c_str();
				i++;
			}
		}
		std::cout << "animNames size:" << animNames.size() << std::endl;
		anim1 = animNames[anim1index];
		anim2 = animNames[anim2index];
		return true;
	}
	else
		return false;
}

bool ReadAnimToSkeleton(std::shared_ptr<SkeletonAsset> skeleton, std::string filename)
{
	std::shared_ptr<OrderedAnimation> anim = std::make_shared<OrderedAnimation>(filename, skeleton);

	int i = 0;
	while (skeleton->orderedAnimations.find(anim->name) != skeleton->orderedAnimations.end())
	{
		if (i == 0)
		{
			anim->name = anim->name + "_1";
		}
		else
		{
			// replace last number
			std::string name = anim->name;
			name.replace(name.end() - 1, name.end(), std::to_string(i));
			anim->name = name;
		}
		i++;
	}

	skeleton->orderedAnimations[anim->name] = anim;
	animNames.emplace_back(anim->name.c_str());

	return true;
}

bool ExportAnim(std::shared_ptr<SkeletonAsset> skeleton, std::string anim, std::string filename)
{
	if (skeleton->orderedAnimations.find(anim) != skeleton->orderedAnimations.end())
	{
		skeleton->orderedAnimations[anim]->Export(filename);
		return true;
	}
	else
		return false;
}

bool DrawAll(const gl::IShader &shader)
{
	for (int i = 0; i < meshNames.size(); i++)
	{
		if (!meshDraw[i])
			continue;

		if (skeletonContext == nullptr)
			throw std::runtime_error("skeletonContext is nullptr");

		if (skeletonContext->orderedMeshes.find(meshNames[i]) != skeletonContext->orderedMeshes.end())
			skeletonContext->orderedMeshes[meshNames[i]]->DrawInstance(shader);
	}
	return true;
}

bool FlushAll(const gl::IShader &shader)
{
	// flush
	for (auto om : skeletonContext->orderedMeshes)
	{
		om.second->FlushInstance();
	}

	for (int i = 0; i < skeletons.size(); i++)
	{
		skeletons[i]->ProcessAllMeshInstance();
	}

	skeletonManager->UpdateAnimTexture();
	skeletonManager->PrepareInstanceDraw(shader);

	return true;
}

int main(int argc, char **argv)
{
	system("chcp 65001");

	// {
	// 	// test file write and read
	// 	FileWriter writer("test.txt");
	// 	writer.Write("hello world");
	// 	writer.WriteBool(true);
	// 	writer.WriteInt32(123);
	// 	writer.WriteFloat32(1.23f);
	// 	writer.Write("end world");
	// 	writer.Close();
	// 	FileReader reader("test.txt");
	// 	reader.ReturnToStart();
	// 	std::string str = reader.ReadString(11);
	// 	bool b = reader.ReadBool();
	// 	int i = reader.ReadInt32();
	// 	float f = reader.ReadFloat32();
	// 	std::string str2 = reader.ReadString(9);
	// 	std::cout << "Testing FileWriter and FileReader: " << std::endl;
	// 	std::cout << str << " " << b << " " << i << " " << f << " " << str2 << std::endl;
	// 	reader.Close();
	// }

	int windowWidth = 1280;
	int windowHeight = 720;
	int SkeletonUpdateDelay = 33;
	bool firstRun = true;
	high_resolution_clock::time_point tlast;

	if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS |
				 SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER | SDL_INIT_SENSOR) != 0)
	{
		printf("Error: %s\n", SDL_GetError());
		return -1;
	}

	const char *glsl_version = "#version 130";
	// SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
	int success1 = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
	checkSDLError();
	int success2 = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
	checkSDLError();
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

	//#endif

	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);

	atexit(SDL_Quit);

	SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
	SDL_Window *window = SDL_CreateWindow("demo", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, windowWidth, windowHeight, window_flags);
	if (window == nullptr)
	{
		printf("Window could not be created! SDL_Error: %s\n", SDL_GetError());
		return -1;
	}
	SDL_GLContext gl_context = SDL_GL_CreateContext(window);
	SDL_GL_MakeCurrent(window, gl_context);
	SDL_GL_SetSwapInterval(1); // Enable vsync

	// Setup Dear ImGui context
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO &io = ImGui::GetIO();
	// (void)io;
	// io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls
	// io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls

	// Setup Dear ImGui style
	ImGui::StyleColorsDark();
	// ImGui::StyleColorsClassic();

	// Setup Platform/Renderer backends
	ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
	ImGui_ImplOpenGL3_Init(glsl_version);

	bool show_fps_window = true;
	bool show_file_window = true;
	ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

	//注册OpenGL函数指针
	if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
	{
		printf("Failed to Load GL Loader for glad!");
		return -1;
	}

	Singleton<Cache<std::string, Texture>>::Instance();

	// LoadModel("bits/testpart/testpart.gltf");

	auto x = glGetString(GL_VERSION);
	printf("glGetString (GL_VERSION) returns %s\n", glGetString(GL_VERSION));
	Shader shader;

	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glEnable(GL_CULL_FACE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	bool runFlag = true;
	SDL_Event event;

	// =====================后处理准备=========================
	Shader screenShader("shader/screen_v.glsl", "shader/screen_f.glsl", "screenShader");

	float quadVertices[] = {// vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
							// positions   // texCoords
							-1.0f, 1.0f, 0.0f, 1.0f,
							-1.0f, -1.0f, 0.0f, 0.0f,
							1.0f, -1.0f, 1.0f, 0.0f,

							-1.0f, 1.0f, 0.0f, 1.0f,
							1.0f, -1.0f, 1.0f, 0.0f,
							1.0f, 1.0f, 1.0f, 1.0f};

	// screen quad VAO
	unsigned int quadVAO, quadVBO;
	glGenVertexArrays(1, &quadVAO);
	glGenBuffers(1, &quadVBO);
	glBindVertexArray(quadVAO);
	glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)0);
	glEnableVertexAttribArray(1);
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void *)(2 * sizeof(float)));

	screenShader.UseShader();
	screenShader.setInt("screenTexture", 0);
	screenShader.setInt("screenDepthTexture", 1);
	screenShader.setInt("sunDepthTexture", 2);

	// framebuffer configuration
	// -------------------------
	unsigned int framebuffer, depthFrameBuffer;
	glGenFramebuffers(1, &framebuffer);
	glGenFramebuffers(1, &depthFrameBuffer);

	glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
	unsigned int textureColorbuffer, textureDepthBuffer;
	textureColorbuffer = CreateFrameTexture(windowWidth, windowHeight);
	textureDepthBuffer = CreateDepthTexture(windowWidth, windowHeight);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureDepthBuffer, 0);
	// unsigned int rbo;
	// rbo = CreateRenderBuffer(windowWidth, windowHeight);
	// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

	int ShadowMapSize = 2048;

	glBindFramebuffer(GL_FRAMEBUFFER, depthFrameBuffer);
	unsigned int depthMap;
	depthMap = CreateDepthTexture(ShadowMapSize, ShadowMapSize);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
		std::cout << "ERROR::DEPTHFRAMEBUFFER:: DepthFramebuffer is not complete!" << std::endl;
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	// ---------------------------------后处理准备结束--------------------------------------

	////     正交投影
	// glm::mat4 proj = glm::ortho(-(float)windowWidth / 600.0f,(float)windowWidth / 600.0f,
	//                              -(float)windowHeight / 600.0f,(float)windowHeight / 600.0f,
	//                              0.1f, 100.0f);
	//  透视投影
	glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f);
	glm::mat4 model(1.0f);
	glm::mat4 view(1.0f);

	float delta = 0.1f;

	// Camera Control Input
	bool CameraRotate = false;
	bool CameraRotateAroundOrigin = false;
	bool CameraZooming = false;
	bool CameraMoveFORWARD = false;
	bool CameraMoveBACK = false;
	bool CameraMoveLEFT = false;
	bool CameraMoveRIGHT = false;
	int MouseRelX = windowWidth / 2;
	int MouseRelY = windowHeight / 2;
	// Camera additional parameters
	float CameraNear = 0.1f;
	float CameraFar = 100.0f;

	// Sun Control
	bool SunCamera = false;
	float ProjScale = 0.01f;
	bool DisplayDepthMap = false;
	glm::vec3 SunPos = glm::vec3(-15.0f, 15.0f, 0.0f);
	glm::mat4 sunView = glm::lookAt(SunPos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	glm::mat4 sunProj = glm::ortho(-(float)ShadowMapSize * ProjScale, (float)ShadowMapSize * ProjScale,
								   -(float)ShadowMapSize * ProjScale, (float)ShadowMapSize * ProjScale,
								   0.1f, glm::length(SunPos) * 2.0f);
	glm::mat4 sunVP = sunProj * sunView;

	// Common Display
	bool DisplayWireframe = false;
	bool DisplayDepth = false;
	bool DisplayPosition = false;
	bool DisplayShadow = false;
	float biasMax = 0.001f;
	float biasMin = 0.0005f;

	// Frame Buffer Display
	bool FrameBufferShadow = false;
	bool FrameBufferPosition = false;
	float FrameShadowBias = 0.003f;

	// lights
	ImVec4 pointlightPos = ImVec4(3.00f, 6.0f, 1.0f, 1.0f);
	ImVec4 sun_color = ImVec4(1.0f, 0.97f, 0.95f, 1.00f);
	ImVec4 spot_color = ImVec4(0.75f, 0.75f, 0.75f, 1.00f);
	ImVec4 point_color = ImVec4(0.85f, 0.8f, 0.7f, 1.00f);
	bool SpotLight = false;

	int TickCounter = 0;
	float blend = 0.0f;

	BodyPartDraw.resize(9, true);

	unsigned int nCpu = std::max(std::thread::hardware_concurrency(), (unsigned int)1);

	ThreadPool pool(nCpu);
	tlast = high_resolution_clock::now();

	//渲染循环
	while (runFlag)
	{
		MouseRelX = 0;
		MouseRelY = 0;

		if (CameraMoveFORWARD)
			camera.ProcessKeyboard(FORWARD, delta);
		if (CameraMoveBACK)
			camera.ProcessKeyboard(BACKWARD, delta);
		if (CameraMoveLEFT)
			camera.ProcessKeyboard(LEFT, delta);
		if (CameraMoveRIGHT)
			camera.ProcessKeyboard(RIGHT, delta);

		proj = glm::perspective(glm::radians(camera.Zoom), (float)windowWidth / (float)windowHeight, CameraNear, CameraFar);

		//处理键盘事件
		while (SDL_PollEvent(&event))
		{
			ImGui_ImplSDL2_ProcessEvent(&event);
			switch (event.type)
			{
			case SDL_QUIT:
				runFlag = false;
				break;
			case SDL_MOUSEBUTTONDOWN:
				if (event.button.button == SDL_BUTTON_MIDDLE)
				{
					SDL_SetRelativeMouseMode(SDL_TRUE);
					CameraRotate = true;
				}
				if (event.button.button == SDL_BUTTON_RIGHT)
				{
					SDL_SetRelativeMouseMode(SDL_TRUE);
					CameraRotate = true;
					if (!CameraRotateAroundOrigin)
					{
						camera.UpdateSurroundPos();
					}
					CameraRotateAroundOrigin = true;
				}
				break;
			case SDL_MOUSEBUTTONUP:
				if (event.button.button == SDL_BUTTON_MIDDLE)
				{
					SDL_SetRelativeMouseMode(SDL_FALSE);
					CameraRotate = false;
				}
				if (event.button.button == SDL_BUTTON_RIGHT)
				{
					SDL_SetRelativeMouseMode(SDL_FALSE);
					CameraRotate = false;
					CameraRotateAroundOrigin = false;
				}
				break;
			case SDL_MOUSEMOTION:
				if (CameraRotate)
				{
					MouseRelX = event.motion.xrel;
					MouseRelY = event.motion.yrel;
					if (CameraRotateAroundOrigin)
						camera.RotateByMouseSurround((float)MouseRelX, -(float)MouseRelY);
					else
						camera.RotateByMouse((float)MouseRelX, -(float)MouseRelY);
				}
				break;
			case SDL_KEYDOWN:
				if (event.key.keysym.sym == SDLK_ESCAPE)
					runFlag = false;
				if (event.key.keysym.sym == SDLK_w)
					CameraMoveFORWARD = true;
				if (event.key.keysym.sym == SDLK_s)
					CameraMoveBACK = true;
				if (event.key.keysym.sym == SDLK_a)
					CameraMoveLEFT = true;
				if (event.key.keysym.sym == SDLK_d)
					CameraMoveRIGHT = true;
				if (event.key.keysym.sym == SDLK_LCTRL)
					CameraZooming = true;
				if (event.key.keysym.sym == SDLK_f)
					SpotLight = !SpotLight;

				if (event.key.keysym.sym == SDLK_SPACE)
				{
					camera.Reset();
				}
				break;
			case SDL_KEYUP:
				if (event.key.keysym.sym == SDLK_w)
					CameraMoveFORWARD = false;
				if (event.key.keysym.sym == SDLK_s)
					CameraMoveBACK = false;
				if (event.key.keysym.sym == SDLK_a)
					CameraMoveLEFT = false;
				if (event.key.keysym.sym == SDLK_d)
					CameraMoveRIGHT = false;
				if (event.key.keysym.sym == SDLK_LCTRL)
					CameraZooming = false;
				break;
			case SDL_MOUSEWHEEL:
				if (CameraZooming)
					camera.ProcessMouseScroll(event.wheel.y);
				break;
			default:
				break;
			}
		}

		RecordTime("Prepare Draw Cost");

		RecordTime("Shader prepare");
		{
			view = camera.GetViewMatrix();
			sunView = glm::lookAt(SunPos, glm::vec3(0.0f, 0.0f, 0.0f), glm::normalize(glm::cross(glm::cross(-SunPos, WorldUp), -SunPos)));
			sunView = glm::translate(sunView, -camera.Position + glm::vec3(0.0f, 0.0f, 10.0f));
			sunProj = glm::ortho(-(float)windowWidth * ProjScale, (float)windowWidth * ProjScale,
								 -(float)windowHeight * ProjScale, (float)windowHeight * ProjScale,
								 0.1f, glm::length(SunPos) * 2.0f);
			sunVP = sunProj * sunView;

			shader.UseShader();
			shader.setVec3("dirLight.direction", glm::normalize(-SunPos));
			shader.setVec3("dirLight.color", glm::vec3(sun_color.x, sun_color.y, sun_color.z));
			shader.setFloat("dirLight.biasMax", biasMax);
			shader.setFloat("dirLight.biasMin", biasMin);

			shader.setVec3("pointLight.position", glm::vec3(pointlightPos.x, pointlightPos.y, pointlightPos.z));
			shader.setVec3("pointLight.color", glm::vec3(point_color.x, point_color.y, point_color.z));
			shader.setFloat("pointLight.constant", 1.0f);
			shader.setFloat("pointLight.linear", 0.009f);
			shader.setFloat("pointLight.quadratic", 0.0032f);

			shader.setVec3("spotLight.position", camera.Position);
			shader.setVec3("spotLight.direction", camera.Front);
			shader.setVec3("spotLight.color", glm::vec3(spot_color.x, spot_color.y, spot_color.z));
			shader.setFloat("spotLight.innerCutoff", glm::cos(glm::radians(5.5f)));
			shader.setFloat("spotLight.outerCutoff", glm::cos(glm::radians(15.5f)));
			shader.setBool("spotLight.turnOn", SpotLight);

			shader.setFloat("material.ambientStrength", 0.3f);
			shader.setFloat("material.diffuseStrength", 1.0f);
			shader.setFloat("material.specularStrength", 0.4f);
			shader.setFloat("material.shininess", 32.0f);

			shader.setMat4("SunVP", sunVP);

			shader.setMat4("mvp.modelMatrix", model);
			shader.setBool("donotCalculateLights", false);

			shader.setBool("DisplayDepth", DisplayDepth);
			shader.setBool("DisplayPosition", DisplayPosition);

			shader.setBool("DisplayShadow", DisplayShadow);
		}
		StopRecord("Shader prepare");

		if (skeletonContext != nullptr && !Processing && (std::chrono::duration_cast<milliseconds>(high_resolution_clock::now() - tlast).count() >= SkeletonUpdateDelay || firstRun))
		{
			Processing = true;
			futures.clear();
			tlast = high_resolution_clock::now();

			RecordTime("Update Animation");

			int animsize = animNames.size();

			for (int i = 0; i < skeletons.size(); i++)
			{
				skeletons[i]->PrepareToDraw();
				//ProcessSkeleton(skeletons[i].get(), glm::translate(model, glm::vec3(0, 0, -i % animsize)), TickCounter, anim1, anim2, blend);
				 ProcessSkeleton(skeletons[i].get(), glm::translate(model, glm::vec3(0, 0, -i * 1)), TickCounter, anim1, anim2, blend);
			}
		}

		if (Processing && skeletonContext != nullptr)
		{
			ProcessOver = true;

			if (ProcessOver)
			{
				StopRecord("Update Animation");
				Processing = false;
				ProcessOver = false;
				RecordTime("Update Instances");

				// flush
				FlushAll(shader);

				StopRecord("Update Instances");
			}
		}

		StopRecord("Prepare Draw Cost");

		TickCounter++;

		if (io.DisplaySize.x < 2.0f || io.DisplaySize.y < 2.0f)
		{
			// 不修改，考虑到这种情况可能是窗口被突然最小化
		}
		else if ((int)io.DisplaySize.x != windowWidth || (int)io.DisplaySize.y != windowHeight)
		{
			windowWidth = io.DisplaySize.x;
			windowHeight = io.DisplaySize.y;
			glViewport(0, 0, windowWidth, windowHeight);

			glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
			textureColorbuffer = CreateFrameTexture(windowWidth, windowHeight);
			textureDepthBuffer = CreateDepthTexture(windowWidth, windowHeight);
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);
			glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureDepthBuffer, 0);
			// rbo = CreateRenderBuffer(windowWidth, windowHeight);
			// glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
			glBindFramebuffer(GL_FRAMEBUFFER, 0);

			// glBindFramebuffer(GL_FRAMEBUFFER, depthFrameBuffer);
			// depthMap = CreateDepthTexture(windowWidth, windowHeight);
			// glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
			// glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}

		RecordTime("Draw");

		if (meshes.size() > 0)
		{
			glDepthFunc(GL_LESS);
			glViewport(0, 0, ShadowMapSize, ShadowMapSize);
			shader.setBool("donotCalculateLights", true);
			shader.setMat4("mvp.projMatrix", sunProj);
			shader.setMat4("mvp.viewMatrix", sunView);
			shader.setVec3("viewPos", SunPos);

			glBindFramebuffer(GL_FRAMEBUFFER, depthFrameBuffer);
			glEnable(GL_DEPTH_TEST);
			glCullFace(GL_FRONT);
			glClear(GL_DEPTH_BUFFER_BIT);

			// for (auto om : modelContext.skeleton->orderedMeshes)
			// {
			// 	om.second->DrawInstance(shader);
			// }
			// for (int i = 0; i < meshNames.size(); i++)
			// {
			// 	if (!meshDraw[i])
			// 		continue;
			// 	if (skeletonContext == nullptr)
			// 		throw std::runtime_error("skeletonContext is nullptr");

			// 	if (skeletonContext->orderedMeshes.find(meshNames[i]) != skeletonContext->orderedMeshes.end())
			// 		skeletonContext->orderedMeshes[meshNames[i]]->DrawInstance(shader);
			// }
			DrawAll(shader);

			glBindFramebuffer(GL_FRAMEBUFFER, 0);
			glDisable(GL_DEPTH_TEST);
			glClear(GL_DEPTH_BUFFER_BIT);
			glCullFace(GL_BACK);
		}

		if (meshes.size() > 0)
		{
			glViewport(0, 0, windowWidth, windowHeight);

			shader.setBool("donotCalculateLights", false);

			glActiveTexture(GL_TEXTURE6);
			shader.setInt("shadowMap", 6);
			glBindTexture(GL_TEXTURE_2D, depthMap);
			glActiveTexture(GL_TEXTURE0);

			if (SunCamera)
			{
				shader.setMat4("mvp.projMatrix", sunProj);
				shader.setMat4("mvp.viewMatrix", sunView);
				shader.setVec3("viewPos", SunPos);
			}
			else
			{
				shader.setMat4("mvp.projMatrix", proj);
				shader.setMat4("mvp.viewMatrix", view);
				shader.setVec3("viewPos", camera.Position);
			}

			glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
			glEnable(GL_DEPTH_TEST);
			if (DisplayWireframe)
				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			else
				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

			glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			// modelContext.Draw(shader, instanceCount);
			// for (auto om : modelContext.skeleton->orderedMeshes)
			// {
			// 	om.second->DrawInstance(shader);
			// }

			// for (int i = 0; i < meshNames.size(); i++)
			// {
			// 	if (!meshDraw[i])
			// 		continue;

			// 	if (skeletonContext == nullptr)
			// 		throw std::runtime_error("skeletonContext is nullptr");

			// 	if (skeletonContext->orderedMeshes.find(meshNames[i]) != skeletonContext->orderedMeshes.end())
			// 		skeletonContext->orderedMeshes[meshNames[i]]->DrawInstance(shader);
			// }
			DrawAll(shader);

			glBindFramebuffer(GL_FRAMEBUFFER, 0);
			glDisable(GL_DEPTH_TEST);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
			// set back to fill mode
			if (DisplayWireframe)
				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		}

		StopRecord("Draw");

		// Start the Dear ImGui frame
		ImGui_ImplOpenGL3_NewFrame();
		ImGui_ImplSDL2_NewFrame();
		ImGui::NewFrame();

		{
			static int click = 0;

			ImGui::Begin("Config");

			// if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
			//     click++;
			// ImGui::SameLine();
			// ImGui::Text("counter = %d", click);

			ImGui::Checkbox("FPS Window", &show_fps_window);
			ImGui::Checkbox("File Window", &show_file_window);

			{
				bool flag = true;
				if (ImGui::TreeNode("Body Part"))
				{
					for (int i = 0; i < BodyPartDraw.size(); i++)
					{
						std::string labal = BodyPartNames[i] + ": ";
						flag = BodyPartDraw[i];
						ImGui::Selectable(labal.c_str(), &flag);
						BodyPartDraw[i] = flag;
					}
					ImGui::TreePop();
				}
			}
			for (int i = 0; i < BodyPartDraw.size(); i++)
			{
				if (BodyPartDraw[i])
				{
					for (auto &skeleton : skeletons)
					{
						skeleton->DrawBodyMask(BodyPartType[i]);
					}
				}
				else
				{
					for (auto &skeleton : skeletons)
					{
						skeleton->HideBodyMask(BodyPartType[i]);
					}
				}
			}

			{
				bool flag = true;
				if (ImGui::TreeNode("Meshes"))
				{
					for (int i = 0; i < meshNames.size(); i++)
					{
						std::string labal = meshNames[i] + ": ";
						flag = meshDraw[i];
						ImGui::Selectable(labal.c_str(), &flag);
						meshDraw[i] = flag;
					}
					ImGui::TreePop();
				}
			}

			ImGui::Checkbox("Debug: ShadowDepthMap", &DisplayDepthMap);
			if (ImGui::IsItemHovered())
			{
				ImGui::BeginTooltip();
				ImGui::Text("Show Sun Depth Map");
				ImGui::EndTooltip();
			}
			ImGui::Checkbox("Debug: Sun Camera", &SunCamera);
			if (ImGui::IsItemHovered())
			{
				ImGui::BeginTooltip();
				ImGui::Text("Switch to Sun Camera");
				ImGui::EndTooltip();
			}
			ImGui::DragFloat("Sun ProjScale", &ProjScale, 0.001f);
			if (ImGui::IsItemHovered())
			{
				ImGui::BeginTooltip();
				ImGui::Text("Sun Camera Projection Scale");
				ImGui::EndTooltip();
			}
			static float SunPosVec3f[3] = {0, 0, 0};
			SunPosVec3f[0] = SunPos.x;
			SunPosVec3f[1] = SunPos.y;
			SunPosVec3f[2] = SunPos.z;
			ImGui::DragFloat3("Draw: Sun Pos", SunPosVec3f, 0.1f, -100.0f, 100.0f);
			SunPos.x = SunPosVec3f[0];
			SunPos.y = SunPosVec3f[1];
			SunPos.z = SunPosVec3f[2];

			ImGui::Checkbox("Debug: Frame Position", &FrameBufferPosition);
			ImGui::Checkbox("Draw: Frame Shadow", &FrameBufferShadow);
			ImGui::DragFloat("Frame Shadow bias", &FrameShadowBias, 0.0001f);

			ImGui::Checkbox("Light: Flashlight", &SpotLight);
			if (ImGui::IsItemHovered())
			{
				ImGui::BeginTooltip();
				ImGui::Text("Click [F] to toggle");
				ImGui::EndTooltip();
			}

			ImGui::ColorEdit3("Light: Sun color", (float *)&sun_color);
			ImGui::DragFloat3("Light: PointLight Pos", (float *)&pointlightPos, 0.1f, -100.0f, 100.0f);
			ImGui::ColorEdit3("Light: PointLight color", (float *)&point_color);
			ImGui::ColorEdit3("Light: FlashLight color", (float *)&spot_color);
			ImGui::Checkbox("Draw: Shadow", &DisplayShadow);
			ImGui::SliderFloat("Shadow: biasMin", &biasMin, 0.00001f, 0.005f);
			ImGui::SliderFloat("Shadow: biasMax", &biasMax, 0.0005f, 0.005f);

			ImGui::Checkbox("Debug: Depth", &DisplayDepth);
			ImGui::Checkbox("Debug: Position", &DisplayPosition);
			ImGui::Checkbox("Debug: WireFrame", &DisplayWireframe);
			ImGui::ColorEdit3("Clear color", (float *)&clear_color);

			static float model_scales = 5.0f;

			ImGui::DragFloat("Model Scale", &model_scales, 0.005f);

			model = glm::mat4(1.0f);
			model = glm::scale(model, glm::vec3(model_scales));

			if (animNames.size() > 0)
			{
				int anim1_id = anim1index;
				int anim2_id = anim2index;

				ImGui::Combo("Anim A", &anim1index, animNames.data(), animNames.size());
				ImGui::Combo("Anim B", &anim2index, animNames.data(), animNames.size());
				if (anim1_id != anim1index || anim2_id != anim2index)
				{
					TickCounter = 0;
					anim1 = animNames[anim1index];
					anim2 = animNames[anim2index];
				}
				ImGui::SliderFloat("Anim Blend", &blend, 0.0f, 1.0f);
			}

			ImGui::Text("How to control your Camera   ");
			if (ImGui::IsItemHovered())
			{
				ImGui::BeginTooltip();
				ImGui::Text("WASD to move");
				ImGui::Text("MiddleClick and drag to roatate");
				ImGui::Text("RightClick and drag to surround");
				ImGui::Text("Hold LeftCtrl and mouse scroll to zoom");
				ImGui::EndTooltip();
			}

			ImGui::DragFloat("Camera Near", &CameraNear, 0.01f);
			ImGui::DragFloat("Camera Far", &CameraFar, 0.01f);

			ImGui::DragFloat("Movement Speed", &camera.MovementSpeed, 0.05f);
			ImGui::DragFloat("Mouse Sensitivity", &camera.MouseSensitivity, 0.005f);
			ImGui::SliderFloat("Zoom", &camera.Zoom, 1.0f, 150.0f);
			if (camera.Zoom > camera.MaxZoom)
				camera.MaxZoom = camera.Zoom;
			ImGui::SliderFloat("MaxZoom", &camera.MaxZoom, 1.0f, 150.0f);
			if (camera.Zoom > camera.MaxZoom)
				camera.Zoom = camera.MaxZoom;

			ImGui::Text("mouse x: %d  y: %d", MouseRelX, MouseRelY);
			ImGui::Text("camera yaw: %.3f  pitch: %.3f", camera.Yaw, camera.Pitch);

			delta = 1 / ImGui::GetIO().Framerate;
			ImGui::End();
		}

		if (show_fps_window)
		{
			ImGui::Begin("FPS Info", &show_fps_window);												 // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
			ImGui::Text("DisplaySize Width: %.2f Height: %.2f", io.DisplaySize.x, io.DisplaySize.y); // Display some text (you can use a format strings too)
			ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
			ImGui::Text("Thread Count %d", nCpu);

			ImGui::Text("Character Count %d", skeletons.size());

			for (auto t : timesDuration)
			{
				ImGui::Text("%s: %d ms", t.first.c_str(), t.second);
			}

			if (ImGui::Button("Close Me"))
				show_fps_window = false;
			ImGui::End();
		}

		if (show_file_window)
		{
			ImGui::Begin("Export/Import", &show_file_window);

			static int import_model_infotick = 0;
			static char *model_path = "bits/testcloth/testcloth.gltf";
			ImGui::InputText("Model Path", model_path, 64);
			if (ImGui::Button("Import Model"))
			{
				LoadModel(model_path);
				import_model_infotick = 180;
			}
			if (import_model_infotick > 0)
			{
				ImGui::TextColored(ImVec4(0.15f, 1.0f, 0.25f, 1.0f), "Import model success");
				import_model_infotick--;
			}

			static int selected = -1;
			static int last_selected = -1;
			static char skeletonAssetType[20] = "";

			if (skeletonContext != nullptr)
			{
				for (int i = 0; i < 20; i++)
				{
					if (skeletonContext->SkeletonType[i] == '\0')
					{
						skeletonAssetType[i] = '\0';
						break;
					}
					else
					{
						skeletonAssetType[i] = skeletonContext->SkeletonType[i];
					}
				}

				ImGui::Text("\n");
				ImGui::InputText("SkeletonAsset", skeletonAssetType, 20);
				static int anim_export = 0;
				static char anim_export_name[64] = "";
				static char anim_import_name[64] = "";

				ImGui::Combo("Anim A", &anim_export, animNames.data(), animNames.size());
				strcpy(anim_export_name, (std::string(animNames[anim_export]) + ".anim").c_str());
				ImGui::InputText("Anim Export Path", anim_export_name, 64);
				if (anim_export_name != "" && ImGui::Button("Export Anim"))
				{
					ExportAnim(skeletonContext, animNames[anim_export], anim_export_name);
				}
				ImGui::Text("\n");
				ImGui::InputText("Anim Import Path", anim_import_name, 64);
				if (anim_import_name != "" && ImGui::Button("Import Anim"))
				{
					ReadAnimToSkeleton(skeletonContext, anim_import_name);
				}
				ImGui::Text("\n");
				skeletonContext->SkeletonType = skeletonAssetType;

				ImGui::Text("\n");
				if (ImGui::Button("Export Skeleton"))
				{
					skeletonContext->Export(skeletonContext->SkeletonType + ".skl");
				}
			}

			if (ImGui::TreeNode("Meshes"))
			{
				for (int i = 0; i < meshNames.size(); i++)
				{
					std::string labal = meshNames[i] + ": ";
					if (ImGui::Selectable(labal.c_str(), selected == i))
						selected = i;
				}
				ImGui::TreePop();
			}

			ImGui::Text("\n");

			// export mesh
			static char export_path[64] = "";
			static int export_info_Tick = 0;
			static bool show_export_info = false;
			if (selected >= 0 && selected < meshNames.size())
			{
				// set the export path
				if (last_selected != selected)
				{
					strcpy(export_path, (meshNames[selected] + ".mesh").c_str());
					last_selected = selected;
				}
				ImGui::InputText("export path", export_path, 64);

				if (show_export_info && export_info_Tick > 0)
				{
					ImGui::TextColored(ImVec4(0.15f, 1.0f, 0.25f, 1.0f), "Export Successful!");
					export_info_Tick--;
				}
				else if (ImGui::Button("Export"))
				{
					std::cout << "Export " << meshNames[selected] << std::endl;
					skeletonContext->orderedMeshes[meshNames[selected]]->ExportToFile(export_path);
					show_export_info = true;
					export_info_Tick = 180;
				}
				else
				{
					export_info_Tick = 0;
					show_export_info = false;
				}
			}
			else
			{
				ImGui::Text("To export a mesh, select it from the list");
				strcpy(export_path, "");
			}

			ImGui::Text("\n");

			// import mesh
			static char import_path[64] = "";
			static int import_info_Tick = 0;
			static bool show_import_info = false;
			static ImVec4 import_info_color = ImVec4(0.15f, 1.0f, 0.25f, 1.0f);
			static char *import_info = "";
			ImGui::InputText("import path", import_path, 64);
			if (import_path[0] != '\0')
			{
				if (show_import_info && import_info_Tick > 0)
				{
					ImGui::TextColored(import_info_color, import_info);
					import_info_Tick--;
				}
				else if (ImGui::Button("Import"))
				{
					std::string name;

					// get the file name of mesh but without the extension and path
					std::string path = import_path;

					if (!FileReader::exists(path))
					{
						std::cout << "file not exists" << std::endl;
						import_info = "File not exists!";
						show_import_info = true;
						import_info_Tick = 180;
						import_info_color = ImVec4(1.0f, 0.25f, 0.15f, 1.0f);
					}
					else
					{
						std::string::size_type pos = path.find_last_of("/\\");
						if (pos != std::string::npos)
							name = path.substr(pos + 1);
						else
							name = path;
						pos = name.find_last_of(".");
						if (pos != std::string::npos)
							name = name.substr(0, pos);

						std::cout << "from " << import_path << " import " << name << std::endl;

						std::shared_ptr<OrderedMesh> mesh = std::make_shared<OrderedMesh>(import_path, skeletonContext);
						mesh->SetupMesh();

						if (skeletonContext->orderedMeshes.find("part_torso") != skeletonContext->orderedMeshes.end())
						{
							mesh->SetTexture(skeletonContext->orderedMeshes["part_torso"]->GetTexture());
						}

						// add to model context skeleton ordered meshes
						int i = 1;
						while (skeletonContext->orderedMeshes.find(name) != skeletonContext->orderedMeshes.end())
						{
							// rename the mesh
							if (i == 1)
								name = name + '_' + std::to_string(i);
							else
								name = name.substr(0, name.find_last_of('_')) + '_' + std::to_string(i);
							i++;
						}

						skeletonContext->orderedMeshes.insert(std::make_pair(name, mesh));
						meshNames.emplace_back(name);
						selected = meshNames.size() - 1;
						meshDraw.emplace_back(true);

						for (int i = 0; i < skeletons.size(); i++)
						{
							meshes.emplace_back(mesh->CreateBindedInstance(skeletons[i]));
						}

						// flush
						FlushAll(shader);
						import_info = "Import Successful!";
						show_import_info = true;
						import_info_Tick = 180;
						import_info_color = ImVec4(0.15f, 1.0f, 0.25f, 1.0f);
					}
				}
				else
				{
					show_import_info = false;
				}
			}
			else
			{
				ImGui::Text("To import a mesh, input the path of the mesh");
			}

			ImGui::End();
		}

		if (meshes.size() > 0)
		{
			screenShader.UseShader();
			screenShader.setBool("FrameBufferShadow", FrameBufferShadow);
			screenShader.setBool("FrameBufferPosition", FrameBufferPosition);
			screenShader.setMat4("SunVP", sunVP);
			screenShader.setMat4("InvCameraVP", glm::inverse(proj * view));
			screenShader.setFloat("FrameShadowBias", FrameShadowBias);

			glActiveTexture(GL_TEXTURE0);
			glBindVertexArray(quadVAO);
			if (DisplayDepthMap)
				glBindTexture(GL_TEXTURE_2D, depthMap);
			else
			{
				glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
				glActiveTexture(GL_TEXTURE1);
				glBindTexture(GL_TEXTURE_2D, textureDepthBuffer);
				glActiveTexture(GL_TEXTURE2);
				glBindTexture(GL_TEXTURE_2D, depthMap);
			}
			glActiveTexture(GL_TEXTURE0);
			glDrawArrays(GL_TRIANGLES, 0, 6);
		}
		else
		{
			glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		}

		// Rendering
		ImGui::Render();
		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

		SDL_GL_SwapWindow(window);

		firstRun = false;
		// SDL_Delay(3000);
	}

	SDL_DestroyWindow(window);
	return 0;
}
